package com.alibaba.nacos.naming.core.v2.index;

import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.MultiSubscriber;
import com.alibaba.nacos.common.utils.ConcurrentHashSet;
import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent;
import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent.*;
import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory;
import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent.ServiceChangedEvent;
import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent.ServiceSubscribedEvent;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class ClientServiceIndexesManager
        extends MultiSubscriber {

    private final Map<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();
    private final Map<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.INSTANCE);
    }

    @Override
    public List<Class<? extends Event>> subscribeTypes() {
        return List.of(ClientRegisterServiceEvent.class, ClientDeregisterServiceEvent.class, ClientSubscribeServiceEvent.class, ClientUnsubscribeServiceEvent.class, ClientReleaseEvent.class);
    }

    @Override
    public void onEvent(Event event) {
        if (event instanceof ClientReleaseEvent) {
            // todo
        }

        if (event instanceof ClientOperationEvent clientOperationEvent) {
            handleClientOperation(clientOperationEvent);
        }
    }

    private void handleClientOperation(ClientOperationEvent event) {
        Service service = event.getService();
        String clientId = event.getClientId();
        if (event instanceof ClientRegisterServiceEvent) {
            publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()).add(clientId);
            NotifyCenter.publishEvent(new ServiceChangedEvent(service));
            return;
        }

        if (event instanceof ClientDeregisterServiceEvent) {
            publisherIndexes.computeIfPresent(service, (key, clientIdSet) -> {
                clientIdSet.remove(clientId);
                NotifyCenter.publishEvent(new ServiceChangedEvent(service, true));
                return clientIdSet.isEmpty() ? null : clientIdSet;
            });
            return;
        }

        if (event instanceof ClientSubscribeServiceEvent) {
            subscriberIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()).add(clientId);
            NotifyCenter.publishEvent(new ServiceSubscribedEvent(service, clientId));
            return;
        }

        if (event instanceof ClientUnsubscribeServiceEvent) {
            subscriberIndexes.computeIfPresent(service, (key, clientIds) -> {
                clientIds.remove(clientId);
                return clientIds.isEmpty() ? null : clientIds;
            });
        }
    }

    public Collection<String> getAllClientsRegisteredService(Service service) {
        return publisherIndexes.getOrDefault(service, new ConcurrentHashSet<>());
    }

    public Collection<String> getAllClientsSubscribeService(Service service) {
        return subscriberIndexes.getOrDefault(service, new ConcurrentHashSet<>());
    }
}

